import React from "react";

export const NavigationContext = React.createContext({});
export const LocationContext = React.createContext({});
export const RouteContext = React.createContext({});

export function useOutlet() {
  const { outlet } = React.useContext(RouteContext);
  return outlet;
}

export function Outlet() {
  return useOutlet();
}

export function useParams() {
  const { matches } = React.useContext(RouteContext);
  const routeMatch = matches[matches.length - 1];
  return routeMatch ? routeMatch.params : {};
}

/**
 *
 * @param {*} children:需要渲染的子节点，location:路径，navigator:导航相关属性
 */
export function Router({ children, location, navigator }) {
  const navigationContext = React.useMemo(() => ({ navigator }), [navigator]);
  const locationContext = React.useMemo(() => ({ location }), [location]);
  return (
    <NavigationContext.Provider value={navigationContext}>
      <LocationContext.Provider value={locationContext} children={children} />
    </NavigationContext.Provider>
  );
}

export function Routes({ children }) {
  return useRoutes(createRoutesFromChildren(children));
}

export function useLocation() {
  return React.useContext(LocationContext).location;
}

export function Route() {
  return null;
}

// export function useRoutes(routes) {
//   const location = useLocation();
//   const pathname = location.pathname;
//   for (let i = 0; i < routes.length; i++) {
//     const { path, element } = routes[i];
//     const match = matchPath({ path }, pathname);
//     if (match) {
//       return element;
//     }
//   }
//   return null;
// }
export function useRoutes(routes) {
  const location = useLocation();
  const pathname = location.pathname;

  const matches = matchRoutes(routes, { pathname });
  console.log("matches", matches);

  return _renderMatches(matches);
}

function _renderMatches(matches) {
  if (!matches) return null;
  // 渲染结果的时候是从右向左执行的
  // matches = [{route: {element: User}}, {route: {element: UserAdd]}}]
  return matches.reduceRight((outlet, match, index) => {
    return (
      <RouteContext.Provider
        value={{ outlet, matches: matches.slice(0, index + 1) }}
      >
        {match.route.element}
      </RouteContext.Provider>
    );
  }, null);
}

function matchRoutes(routes, location) {
  // 扁平化树型结构的路径
  const branches = flattenRoutes(routes);
  console.log("branches", branches);
  const pathname = location.pathname;

  // 路由的优先等级
  rankRouteBranches(branches);

  // 匹配的结果
  let matches = null;
  // 按分支顺序依次进行匹配，如果匹配上了，直接退出循环，不再进行后续的匹配
  for (let i = 0; matches == null && i < branches.length; i++) {
    matches = matchRouteBranch(branches[i], pathname);
  }
  return matches;
}

function rankRouteBranches(branches) {
  branches.sort((a, b) => {
    // 如果分数一样，只能比过索引; 如果分数不一样，按分数倒序排列
    return a.score !== b.score
      ? b.score - a.score
      : compareIndexes(
          a.routesMeta.map((meta) => meta.childrenIndex),
          b.routesMeta.map((meta) => meta.childrenIndex)
        );
  });
}

/**
 * /user/add   routesMeta=[userMeta,addMeta]=>[2,0]
 * /user/list  routesMeta = [userMeta,listMeta]=>[2,1];
 */
function compareIndexes(a, b) {
  //如果级别数量相等，并且父亲都 一样，说是他们是兄弟
  const sibling =
    a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]);
  //如果是兄弟的话，那和比索引，索引越小级别越高，索引越大，级别越低
  //如果不是兄弟，那就认为相等的
  return sibling ? a[a.length - 1] - b[b.length - 1] : 0;
}

/**
 * 用分支的路径匹配地址栏的路径名 url=/user/add/update 与 routesMeta = ['/user', '/add', 'update']
 * @param {*} branch
 * @param {*} pathname 完整路径
 */
function matchRouteBranch(branch, pathname) {
  const { routesMeta } = branch;
  // 此分支路径参数对象  path=/:a/:b/:c  pathname=/vA/vB/vC
  const matchesParams = {}; // {a:vA, b:vB, c:vC}
  const matches = [];
  let matchedPathname = "/"; // 匹配的路径名

  for (let i = 0; i < routesMeta.length; i++) {
    // 获取当前的 meta
    const meta = routesMeta[i];
    // 判断是否是最后一个 meta
    const end = i === routesMeta.length - 1;
    // 获取剩下的将要匹配的路径
    const remainingPathname =
      matchedPathname === "/"
        ? pathname
        : pathname.slice(matchedPathname.length);
    const match = matchPath(
      { path: meta.relativePath, end },
      remainingPathname
    );
    // 如果没有匹配上
    if (!match) return null;

    Object.assign(matchesParams, match.params);

    matches.push({
      params: matchesParams,
      pathname: joinPaths([matchedPathname, match.pathname]),
      pathnameBase: joinPaths([matchedPathname, match.pathnameBase]),
      route: meta.route,
    });
    if (match.pathnameBase !== "/") {
      matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);
    }
  }
  return matches;
}

/**
 * 扁平化树形结构的路由
 * @param {*} routes 树形结构的路由配置
 * @param {*} branches 扁平化后的路由数组
 * @param {*} parentsMeta 父路由的数据
 * @param {*} parentPath 父路由字符串
 * @returns
 */
function flattenRoutes(
  routes,
  branches = [],
  parentsMeta = [],
  parentPath = ""
) {
  routes.forEach((route, index) => {
    // 定义路由元数据
    const meta = {
      route, // 路由对象
      childrenIndex: index,
      relativePath: route.path || "", // 路径相对父路径的路径 UserAdd relativePath=add
    };
    // 该案例中，routes 其实只有一个元素 /user，  parentPath=''  relativePath=/user
    // path=/user
    // 把父路径加上自己的相对路径构建成匹配的完整路径
    const path = joinPaths([parentPath, meta.relativePath]);
    // 在父 meta 数组中添加自己这个 meta
    const routesMeta = parentsMeta.concat(meta);
    // 如果有子路由的话，递归添加到 branches 分支数组中
    if (route.children && route.children.length > 0) {
      flattenRoutes(route.children, branches, routesMeta, path);
    }

    branches.push({
      path,
      routesMeta,
      score: computeScore(path, route.index),
    });
  });

  return branches;
}

function joinPaths(paths) {
  // ['/user/','/add'] => /user//add => /user/add
  return paths.join("/").replace(/\/\/+/g, "/");
}

const isSplat = (s) => s === "*";
const splatPenalty = -2;
const indexRouteValue = 2;
const paramRe = /^:\w+$/;
const dynamicSegmentValue = 3;
const emptySegmentValue = 1;
const staticSegmentValue = 10;
function computeScore(path, index) {
  const segments = path.split("/"); // /user/add   => ['user','add']
  let initialScore = segments.length; //分片的长度就是基础分数
  if (segments.some(isSplat)) {
    //  /user/* 有星说是通配，分数分降低
    initialScore += splatPenalty;
  }
  if (index) {
    initialScore += indexRouteValue;
  }
  return segments
    .filter((s) => !isSplat(s))
    .reduce((score, segment) => {
      return (
        score +
        (paramRe.test(segment)
          ? dynamicSegmentValue
          : segment === ""
          ? emptySegmentValue
          : staticSegmentValue)
      );
    }, initialScore);
}

// export function matchPath({ path }, pathname) {
//   const matcher = compilePath(path);
//   const [name, query] = pathname.split("?");
//   console.log("query", query);
//   const match = name.match(matcher);
//   if (!match) return null;
//   return match;
// }
export function matchPath({ path, end }, pathname) {
  const [matcher, paramNames] = compilePath(path, end);
  const [name, query] = pathname.split("?");
  const match = name.match(matcher);
  if (!match) return null;
  const matchedPathname = match[0];
  let pathnameBase = match[0].replace(/(.)\/+$/, "$1");
  const values = match.slice(1);
  const captureGroups = match.slice(1);
  const params = paramNames.reduce((memo, paramName, index) => {
    if (paramName === "*") {
      const startValue = captureGroups[index] || "";
      pathnameBase = matchedPathname
        .slice(0, matchedPathname.length - startValue.length)
        .replace(/(.)\/+$/, "$1");
    }
    memo[paramName] = values[index];
    return memo;
  }, {});
  return { params, pathname: matchedPathname, pathnameBase };
}

// export function compilePath(path) {
//   const regexpSource = "^" + path + "$";
//   const matcher = new RegExp(regexpSource);
//   return matcher;
// }
function compilePath(path, end) {
  const paramNames = [];
  let regexpSource =
    "^" +
    path
      .replace(/\/*\*?$/, "") // 把 /*，//*， * 全部转为空  /user/* /user* /user//* /user
      .replace(/^\/*/, "/") // 把开始多个/或者说没有/转成一个/   /user：不变 //user：变/user  user：变/user
      .replace(/:(\w+)/g, (_, key) => {
        paramNames.push(key);
        return "([^\\/]+)";
      });
  if (path.endsWith("*")) {
    paramNames.push("*");
    regexpSource +=
      path === "*" || path === "/*" ? "(.*)$" : "(?:\\/(.+)|\\/*)$";
  } else {
    // console.log("end", end);
    regexpSource += end ? "\\/*$" : "(?:\b|\\/|$)";
  }
  // console.log(regexpSource);
  const matcher = new RegExp(regexpSource);
  return [matcher, paramNames];
}

export function createRoutesFromChildren(children) {
  const routes = [];

  React.Children.forEach(children, (child) => {
    // 拿到 Route 标签上的相关属性
    const route = {
      path: child.props.path,
      element: child.props.element,
    };

    // 嵌套路由
    if (child.props.children) {
      route.children = createRoutesFromChildren(child.props.children);
    }
    routes.push(route);
  });

  return routes; // routes = [{path, element, children: []}]
}

export function useSearchParams() {
  const location = useLocation();
  const pathname = location.pathname;
  return new URLSearchParams(pathname.split("?")[1]);
}

export function useNavigate() {
  const { navigator } = React.useContext(NavigationContext);
  const navigate = React.useCallback((to) => navigator.push(to), [navigator]);
  return navigate;
}
